/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.thesis.utils;

import com.thesis.algorithm.*;
import java.util.*;

/**
 *
 * @author Aya Hikaru
 */
public class VelocityEstimateSequence {

    private boolean DEBUGGING = false;
    static public double MAXALLOWVELOCITYURBAN = (50 / 3600.0);
    static public double MAXALLOWVELOCITYRURAL = (65 / 3600.0);
    static public double MAXDIFFERENT = (10/3600.0);
    static public int MINUTECHECKBACK = 120;
//    static public double CHECKBACKFORMAX = 600;
    static public double CHANGEPERCENTAGE = 0.5;

    private void Add(int timeStamp, char State) {
        Enumeration<Character> f;
//        double maxallow = this.MaxVelocityList() + MAXDIFFERENT;
        double maxallow;
        if (buscontroller.isUrban(State) == true)
        {
//            if (maxallow > MAXALLOWVELOCITYURBAN) {
                maxallow = MAXALLOWVELOCITYURBAN;
//            }
        } else {
//            if (maxallow > MAXALLOWVELOCITYRURAL) {
                maxallow = MAXALLOWVELOCITYRURAL;
//            }
        }
        double tmp = 0;
        if (DEBUGGING) {
             System.out.println("ADD AT TIMESTAMP " + timeStamp + " STATE " + (int)State );
        }
        if (lastBusstops == 0 || lastTimeStamp == -1) {
            velocityList.add(new VelocityElement(timeStamp, 0.0));
        } else {
            // check if the same
            if (State == lastBusstops) {
                if (velocityList.size() == 1 && beforelastTimeStamp == -1) {
                    velocityList.removeLast();
                    velocityList.add(new VelocityElement(timeStamp, 0.0));
                } else {
                        LinkedList<Character> tmpllc;
                        tmpllc = computedpair.get(beforelastBusstops);
                        tmpllc.remove((Character) lastBusstops);
                        if (tmpllc.size() == 0) {
                            computedpair.remove(beforelastBusstops);
                        }
                    
                    if (DEBUGGING) {
                        System.out.println("THESAMECASE: ");
                        System.out.println("computedpair: ");
                        f = computedpair.keys();
                        while (f.hasMoreElements()) {
                            Character me = f.nextElement();
                            System.out.println("==" + (int) me + "==" + this.printLinkedListCharacter(computedpair.get(me)));
                        }
                        System.out.println("lastcomputedpair: ");                        
                        System.out.println("==" + (int) beforelastBusstops + "==" + (int) lastBusstops);
                        
                    }
                    tmp = calculateVelocity(beforelastTimeStamp, beforelastBusstops, timeStamp, State);
                    velocityList.removeLast();
                    velocityList.add(new VelocityElement(timeStamp, tmp));
                }
            } else {
                tmp = calculateVelocity(lastTimeStamp, lastBusstops, timeStamp, State);
                velocityList.add(new VelocityElement(timeStamp, tmp));
                beforelastBusstops = lastBusstops;
                beforelastTimeStamp = lastTimeStamp;
            }
        }
        if (DEBUGGING)
        {
            System.out.println("MAXALLOW: " + maxallow *3600);
        }
        if (tmp > maxallow && maxallow > (MAXDIFFERENT + 0.000001)) {
            velocityList.getLast().velocity = maxallow;
        }
        
        lastTimeStamp = timeStamp;
        lastBusstops = State;
        if (DEBUGGING) {
            System.out.println("FINISHED: ");
            System.out.println("computedpair: ");
            f = computedpair.keys();
            while (f.hasMoreElements()) {
                Character me = f.nextElement();
                System.out.println("==" + (int) me + "==" + this.printLinkedListCharacter(computedpair.get(me)));
            }
            System.out.println("lastcomputedpair: ");
            System.out.println("==" + (int) beforelastBusstops + "==" + (int) lastBusstops);


            System.out.println("=======VELOCITY LIST=========");
            for (VelocityElement ve : velocityList) {
                System.out.print(ve.timeStamp + "==" + ve.velocity * 3600 + " ");
            }
            System.out.println();
            System.out.println("+++++VELOCITY LIST END+++++++");
        }
    }

    private void clear() {
        velocityList.clear();
        computedpair.clear();
        lastBusstops = 0;
        lastTimeStamp = -1;
        beforelastTimeStamp = -1;
        beforelastBusstops = 0;
        BusRouteID = 0;
    }

    private BusControl buscontroller;
    protected LinkedList<VelocityElement> velocityList = new LinkedList<VelocityElement>();
    private Hashtable<Character, LinkedList<Character>> computedpair = new Hashtable<Character, LinkedList<Character>>();
    char lastBusstops = 0;
    public int lastTimeStamp = -1;
    char beforelastBusstops = 0;
    int beforelastTimeStamp = -1;
    short BusRouteID = 0;

    public VelocityEstimateSequence(BusControl bc) {
        buscontroller = bc;
    }

    private String printLinkedListCharacter(LinkedList< Character> llc) {
        String result = "{";
        for (char c : llc) {
            result += ((int) (c)) + ",";
        }
        result = result.substring(0, result.length() - 1);
        result += "}";
        return result;

    }

    public void Add(Sequence seq, short busRouteID) {
        this.clear();
        if (this.buscontroller.getBusRoute(busRouteID) == null) {
            return;
        }
        this.BusRouteID = busRouteID;
        int timestamp = 0;
        for (SequenceElement se : seq.sequenceinFile) {
            this.Add(timestamp, se.State);
            timestamp += (int) se.Repetition;
        }
        this.Add(timestamp, seq.sequenceinFile.getLast().State);
    }

    private double calculateVelocity(int fromTimeStamp, char fromBusStop, int toTimeStamp, char toBusStop) {
        double distance = 0;
        BusRoute br = null;
        int previndex = 0;
        int currentindex = 0;
        LinkedList<Character> tmpllc = null;

        br = buscontroller.getBusRoute(BusRouteID);
        if (br != null) {
            previndex = br.BusStopIDList.indexOf(fromBusStop);

            if (previndex != -1) {
                currentindex = br.BusStopIDList.indexOf(toBusStop);
                if (DEBUGGING) {
                    System.out.println("BusRoute: " + BusRouteID + " from BusStop: " + (int) fromBusStop + " at " + previndex + " to BusStop: " + (int) toBusStop + " at " + currentindex);
                }
                if (currentindex > previndex) {
                    tmpllc = computedpair.get(fromBusStop);
                    if (tmpllc == null) {
                        tmpllc = new LinkedList<Character>();
                    }
                    if (!tmpllc.contains(toBusStop)) {
                        tmpllc.add(toBusStop);
                        computedpair.put(fromBusStop, tmpllc);

                        distance = buscontroller.getDistanceBetweenBusStops(fromBusStop, toBusStop, BusRouteID);
//                        distance = buscontroller.getDistanceBetweenBusStops(fromBusStop, toBusStop);
                        if (DEBUGGING) {
                            System.out.println("distance: " + distance);
                        }
                        if (distance < .000001) {
                            System.err.println("BusRoute: " + BusRouteID + " from BusStop: " + (int) fromBusStop + " at " + previndex + " to BusStop: " + (int) toBusStop + " at " + currentindex + "Distance is 0?");

                        }
                    }
                }
            }
        }

        if (DEBUGGING) {
            System.out.println("FINAL: SECONDS FROM " + fromTimeStamp + " TO " + toTimeStamp + " RESULT: " + (distance / (toTimeStamp - fromTimeStamp)) * 3600);
        }
        return (distance / (toTimeStamp - fromTimeStamp));

    }
    

    public double returnVelocity(int noOfLastBusstops) {
        if (noOfLastBusstops >= velocityList.size() || velocityList.size() < 2)
        {
            return 0;
        }
        else
        {
            
            int size = velocityList.size();
            double Sum = 0;
            int time = 0;
            VelocityElement current = null;
            VelocityElement prev = null;
            for (int i = 0; i < noOfLastBusstops; i++)
            {
                current = velocityList.get(size-i-1);
                prev = velocityList.get(size-i-2);
                time += (current.timeStamp - prev.timeStamp);
                Sum += (current.timeStamp - prev.timeStamp) * current.velocity;
            }
            
            lastTimeStamp = prev.timeStamp;
            if (time != 0) {
              return fixLargeLast(Sum/time, noOfLastBusstops);
//                return Sum/time;
            }
            else 
            {
                if (DEBUGGING)
                {
                    System.out.println("WRONG--------------TIME IS 0");
                }
                return 0.0;
            }
        }
    }
    
    public int returnArrivalTime (char toBusStop, int noOfLastBusstops)
    {
        double distance = 0;
        BusRoute br = null;
        int previndex = 0;
        int currentindex = 0;
        br = buscontroller.getBusRoute(BusRouteID);
        char fromBusStop = 0;
        int fromTimeStamp = 0;
        double velocity = returnVelocity(noOfLastBusstops);
        if (velocity < 0.000001)
            return 0;
//        if (velocityList.getLast().timeStamp - velocityList.get(velocityList.size()-2).timeStamp < MIN_INTERVAL_LAST_BUSSTOP)
//        {
//            if (beforelastBusstops == 0 || beforelastTimeStamp == -1)
//                return 0;
//            fromBusStop = beforelastBusstops;
//            fromTimeStamp = beforelastTimeStamp;
//        }
//        else
//        {
            if (lastTimeStamp == -1 || lastBusstops == 0)
                return 0;
            fromBusStop = lastBusstops;
            fromTimeStamp = lastTimeStamp;
//        }
        if (br != null) {
            previndex = br.BusStopIDList.indexOf(fromBusStop);

            if (previndex != -1) {
                currentindex = br.BusStopIDList.indexOf(toBusStop);
                if (DEBUGGING) {
                    System.out.println("BusRoute: " + BusRouteID + " from BusStop: " + (int) fromBusStop + " at " + previndex + " to BusStop: " + (int) toBusStop + " at " + currentindex);
                }
                if (currentindex > previndex) {
                    if ((currentindex - previndex) == 1)
                    {
                        if (fromBusStop == beforelastBusstops)
                            return -1;
                        else 
                        {
                            distance = buscontroller.getDistanceBetweenBusStops(br.BusStopIDList.get(previndex), br.BusStopIDList.get(currentindex));
                            return (int)(distance/velocity);
                        }                          
                    }
                    else {
                        distance = 0;
                        for (int i = previndex; i < currentindex; i++)
                        {
                            distance += buscontroller.getDistanceBetweenBusStops(br.BusStopIDList.get(i), br.BusStopIDList.get(i+1));
                        }
                        if (DEBUGGING) {
                            System.out.println("distance: " + distance);
                        }
                        if (distance < .000001) {
                            System.err.println("BusRoute: " + BusRouteID + " from BusStop: " + (int) fromBusStop + " at " + previndex + " to BusStop: " + (int) toBusStop + " at " + currentindex + "Distance is 0?");
                        }
                        return (int) (distance/velocity - (velocityList.getLast().timeStamp - fromTimeStamp));
                    }
                } else {
                    return -1;
                }
            }
        }
        return 0;
    }
    
    public String returnArrivalTimeString (char BusStop, int noOfLastBusstops)
    {
        int tmp = returnArrivalTime(BusStop, noOfLastBusstops);
        if (tmp == -1)
            return "Passed";
        if (tmp == 0)
            return "Insufficient information";
        tmp /= 60;
        for (int i = 0; i < 30; i++)
        {
            if (i*5 <= tmp && tmp <(i+1)*5)
                return "Arrive in less than " + (i+1)*5 + "minutes";
        }
        return "Too far";
    }

    public void Print() {
        for (VelocityElement ve : velocityList) {
            System.out.println("At timestamp: " + ve.timeStamp + " with velocity : " + ve.velocity * 3600);
        }
    }

    private double fixLargeLast(double d, int noOfLastBusstops) {
        if (d < 0.000001) {
            return d;
        }
//        LinkedList<Double> velocitySorted = new LinkedList<Double>();
//        int size = velocityList.size();
//        VelocityElement current = null;
//        VelocityElement prev = null;
//        for (int i = 0; i < size ; i++) {
//            current = velocityList.get(size - i - 1);
//            if (current.timeStamp < (velocityList.getLast().timeStamp - MINUTECHECKBACK)) {
//                break;
//            }
//            velocitySorted.add(current.velocity);
//        }
//        Collections.sort(velocitySorted);
//        double tmp = 0;
//        if (velocitySorted.size()%2 == 1) {
//            tmp = velocitySorted.get(velocitySorted.size()/2);
//        }
//        else if (velocitySorted.size() == 0) {
//            tmp = d;
//        }
//        else {
//            tmp += velocitySorted.get(velocitySorted.size()/2 - 1);
//            tmp += velocitySorted.get(velocitySorted.size()/2);
//            tmp /=2;
//        }
        double average = 0;
        if ((velocityList.getLast().timeStamp - MINUTECHECKBACK) < (velocityList.get(velocityList.size() - 2).timeStamp))
            average = this.returnVelocity(velocityList.getLast().timeStamp - MINUTECHECKBACK, velocityList.get(velocityList.size() - 2).timeStamp);
        else
            average = d;
//        if (Math.abs(tmp - average) > Math.abs(tmp - d))
//        {
//            average = tmp;
//        }
        if ((d - average) > MAXDIFFERENT) {
            return (average + CHANGEPERCENTAGE * (d-average));
        }
        else {
            return d;
        }
        
    }
    public double returnVelocity(int FromTimeStamp, int ToTimeStamp) {
        double Sum = 0;
        int count = 0;
        if (FromTimeStamp < 0) {
            FromTimeStamp = 0;
        }
        int prevTimeStamp = -1;
        for (VelocityElement ve : velocityList) {
            if (ve.timeStamp <= FromTimeStamp) {
                
            } else if (ve.timeStamp <= ToTimeStamp) {
                if (prevTimeStamp <= FromTimeStamp) {
                    if (ve.velocity > 0.000001)
                    {
                    Sum += (ve.velocity * (ve.timeStamp - FromTimeStamp));
                    count += (ve.timeStamp - FromTimeStamp);
                    }
                } else {
                    if (ve.velocity > 0.000001)
                    {
                    Sum += (ve.velocity * (ve.timeStamp - prevTimeStamp));
                    count += (ve.timeStamp - prevTimeStamp);
                    }
                }
            } else {
                if (prevTimeStamp >= ToTimeStamp) {
                    break;
                } else {
                    if (ve.velocity > 0.000001)
                    {
                    Sum += (ve.velocity * (ToTimeStamp - prevTimeStamp));
                    count += (ToTimeStamp - prevTimeStamp);
                    }
                    break;
                    
                }
            }
            prevTimeStamp = ve.timeStamp;
        }
        if (count == 0) {
            return 0;
        } else {
            return (Sum / count);
        }
    }
//    private double MaxVelocityList() {
//        double max = 0;
//        int size = velocityList.size();
//        //int sum = 0;
//        VelocityElement current = null;
//        for (int i = 1; i < size ; i++) {
//            current = velocityList.get(size - i - 1);
//            if (current.timeStamp < (velocityList.getLast().timeStamp - CHECKBACKFORMAX)) {
//                break;
//            }
//            if (current.velocity > max) 
//                max = current.velocity;
//        }
//        return max;
//    }

}
